package cn.backflow.admin.service;

import cn.backflow.admin.Constants;
import cn.backflow.admin.entity.ResetRecord;
import cn.backflow.admin.entity.User;
import cn.backflow.admin.repository.PermissionRepository;
import cn.backflow.admin.repository.ResetRecordRepository;
import cn.backflow.admin.repository.RoleRepository;
import cn.backflow.admin.repository.UserRepository;
import cn.backflow.data.pagination.Page;
import cn.backflow.data.pagination.PageRequest;
import cn.backflow.data.service.AbstractService;
import cn.backflow.data.service.BaseService;
import cn.backflow.utils.Dates;
import cn.backflow.utils.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class UserService extends AbstractService<User, Integer> implements BaseService<User, Integer> {

    private final ResetRecordRepository resetRecordRepo;
    private final PermissionRepository permissionRepo;
    private final UserRepository userRepo;
    private final RoleRepository roleRepo;

    @Autowired
    public UserService(ResetRecordRepository rr, PermissionRepository p, UserRepository u, RoleRepository r) {
        this.resetRecordRepo = rr;
        this.permissionRepo = p;
        this.userRepo = u;
        this.roleRepo = r;
    }

    public Page<User> query(PageRequest pr) {
        return userRepo.query(pr);
    }

    public List<User> search(PageRequest pr) {
        return userRepo.search(pr);
    }

    @Cacheable(cacheNames = Constants.PERMISSION_CACHE, key = "'user_' + #user.id")
    public Set<String> findPermissionByUser(User user) {
        List<String> permissions = user.isAdmin() ?
                permissionRepo.findCode(null) :
                roleRepo.findPermission(Collections.singletonMap("roleId", user.getRoleId()));
        return new HashSet<>(permissions);
    }

    public User getByEmail(String email) {
        return userRepo.getByIdentity(Collections.singletonMap("email", email));
    }

    public User getByPhone(String phone) {
        return userRepo.getByIdentity(Collections.singletonMap("phone", phone));
    }

    public User getByName(String name) {
        return userRepo.getByIdentity(Collections.singletonMap("name", name));
    }

    /**
     * 验证重置邮箱与密钥是否有效
     *
     * @param email    邮箱
     * @param resetKey 重置密钥
     * @return 是否有效
     */
    public boolean isResetRecordValid(String email, String resetKey) {
        ResetRecord record = resetRecordRepo.getNewestByEmail(email);
        // String key = generateResetKey(email, record.getName(), record.getRequestTime());
        return record != null &&
                record.getValid() == 1 && // 有效状态值为1
                record.getResetKey().equals(resetKey) && //  重置密钥相同
                record.getExpirationTime().compareTo(new Date()) >= 0; // 在有效期内
    }

    /**
     * 生成密码重置密钥
     *
     * @param email       邮箱
     * @param name        用户名
     * @param requestTime 申请重置时间
     * @return 生成的密钥
     */
    private String generateResetKey(String email, String name, Date requestTime) {
        return Strings.md5(email, name + requestTime.toString().replaceAll("\\s", ""));
    }

    /**
     * 保存密码重置记录
     *
     * @param user 申请用户
     */
    @Transactional
    public ResetRecord saveResetResord(User user) {
        Date now = new Date();
        Date expire = Dates.shiftTime(now, Calendar.DAY_OF_YEAR, 1);
        ResetRecord record = new ResetRecord();
        record.setEmail(user.getEmail());
        record.setResetKey(generateResetKey(user.getEmail(), user.getName(), now));
        record.setRequestTime(now);
        record.setExpirationTime(expire);
        resetRecordRepo.insert(record);
        return record;
    }

    @Override
    @Transactional
    public int save(User user) throws DataAccessException {
        String password = Strings.md5(user.getPass(), user.getName());
        user.setPass(password);
        return userRepo.insert(user);
    }

    @Override
    @Transactional
    public int update(User user) throws DataAccessException {
        if (Strings.isNotBlank(user.getPass())) {
            String password = Strings.md5(user.getPass(), user.getName());
            user.setPass(password);
        }
        return userRepo.updateSelective(user);
    }

    @Transactional
    public int updateProfile(User user) {
        user.setPass(Strings.isBlank(user.getPass()) ?
                null :
                Strings.md5(user.getPass(), user.getName())
        );
        return userRepo.updateSelective(user);
    }

    @Transactional
    public int resetPass(String email, String pass, String resetKey) {
        User target = userRepo.getByIdentity(Collections.singletonMap("email", email));
        pass = Strings.md5(pass, target.getName());
        User user = new User();
        user.setId(target.getId());
        user.setPass(pass);
        resetRecordRepo.updateValidState("email", email, 0);
        return userRepo.updateByIdentitySelective(user);
    }

    @Transactional
    public void updateVisited(Integer id) {
        User user = new User();
        user.setId(id);
        user.setVisited(new Date());
        userRepo.updateSelective(user);
    }
}